home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / UpdateManager / UpdateManager.py < prev   
Text File  |  2009-11-02  |  36KB  |  943 lines

  1. # UpdateManager.py 
  2. #  
  3. #  Copyright (c) 2004-2008 Canonical
  4. #                2004 Michiel Sikkes
  5. #                2005 Martin Willemoes Hansen
  6. #  
  7. #  Author: Michiel Sikkes <michiel@eyesopened.nl>
  8. #          Michael Vogt <mvo@debian.org>
  9. #          Martin Willemoes Hansen <mwh@sysrq.dk>
  10. #  This program is free software; you can redistribute it and/or 
  11. #  modify it under the terms of the GNU General Public License as 
  12. #  published by the Free Software Foundation; either version 2 of the
  13. #  License, or (at your option) any later version.
  14. #  This program is distributed in the hope that it will be useful,
  15. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #  GNU General Public License for more details.
  18. #  You should have received a copy of the GNU General Public License
  19. #  along with this program; if not, write to the Free Software
  20. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. #  USA
  22.  
  23. import pygtk
  24. pygtk.require('2.0')
  25. import gtk
  26. import gtk.gdk
  27. import gconf
  28. import gobject
  29. import glib
  30.  
  31. import warnings
  32. warnings.filterwarnings("ignore", "Accessed deprecated property", DeprecationWarning)
  33. import apt
  34. import apt_pkg
  35.  
  36. import gettext
  37. import copy
  38. import string
  39. import sys
  40. import os
  41. import os.path
  42. import stat
  43. import re
  44. import locale
  45. import tempfile
  46. import pango
  47. import subprocess
  48. import pwd
  49. import urllib2
  50. import httplib
  51. import socket
  52. import time
  53. import thread
  54. import xml.sax.saxutils
  55.  
  56. import dbus
  57. import dbus.service
  58. import dbus.glib
  59.  
  60. import GtkProgress
  61. import backend
  62.  
  63. from gettext import gettext as _
  64. from gettext import ngettext
  65.  
  66.  
  67. from Core.utils import *
  68. from Core.UpdateList import UpdateList
  69. from Core.MyCache import MyCache, NotEnoughFreeSpaceError
  70. from Core.MetaRelease import Dist
  71.  
  72. from DistUpgradeFetcher import DistUpgradeFetcherGtk
  73. from ChangelogViewer import ChangelogViewer
  74. from SimpleGtkbuilderApp import SimpleGtkbuilderApp
  75. from HelpViewer import HelpViewer
  76. from MetaReleaseGObject import MetaRelease
  77.  
  78. #import pdb
  79.  
  80. # FIXME:
  81. # - kill "all_changes" and move the changes into the "Update" class
  82.  
  83. # list constants
  84. (LIST_CONTENTS, LIST_NAME, LIST_PKG, LIST_ORIGIN) = range(4)
  85.  
  86. # actions for "invoke_manager"
  87. (INSTALL, UPDATE) = range(2)
  88.  
  89.  
  90. class UpdateManagerDbusControler(dbus.service.Object):
  91.     """ this is a helper to provide the UpdateManagerIFace """
  92.     def __init__(self, parent, bus_name,
  93.                  object_path='/org/freedesktop/UpdateManagerObject'):
  94.         dbus.service.Object.__init__(self, bus_name, object_path)
  95.         self.parent = parent
  96.  
  97.     @dbus.service.method('org.freedesktop.UpdateManagerIFace')
  98.     def bringToFront(self):
  99.         self.parent.window_main.present()
  100.         return True
  101.  
  102. class UpdateManager(SimpleGtkbuilderApp):
  103.  
  104.   def __init__(self, datadir, options):
  105.     self.setupDbus()
  106.     gtk.window_set_default_icon_name("update-manager")
  107.  
  108.     self.datadir = datadir
  109.     SimpleGtkbuilderApp.__init__(self, datadir+"glade/UpdateManager.ui",
  110.                                  "update-manager")
  111.     gettext.bindtextdomain("update-manager", "/usr/share/locale")
  112.     gettext.textdomain("update-manager")
  113.     try:
  114.         locale.setlocale(locale.LC_ALL, "")
  115.     except:
  116.         logging.exception("setlocale failed")
  117.  
  118.     self.image_logo.set_from_icon_name("update-manager", gtk.ICON_SIZE_DIALOG)
  119.     self.window_main.set_sensitive(False)
  120.     self.window_main.grab_focus()
  121.     self.button_close.grab_focus()
  122.     self.dl_size = 0
  123.  
  124.     # create text view
  125.     self.textview_changes = ChangelogViewer()
  126.     self.textview_changes.show()
  127.     self.scrolledwindow_changes.add(self.textview_changes)
  128.     changes_buffer = self.textview_changes.get_buffer()
  129.     changes_buffer.create_tag("versiontag", weight=pango.WEIGHT_BOLD)
  130.  
  131.     # expander
  132.     self.expander_details.connect("notify::expanded", self.activate_details)
  133.  
  134.     # useful exit stuff
  135.     self.window_main.connect("delete_event", self.close)
  136.     self.button_close.connect("clicked", lambda w: self.exit())
  137.  
  138.     # the treeview (move into it's own code!)
  139.     self.store = gtk.ListStore(str, str, gobject.TYPE_PYOBJECT, 
  140.                                gobject.TYPE_PYOBJECT)
  141.     self.treeview_update.set_model(self.store)
  142.     self.treeview_update.set_headers_clickable(True);
  143.  
  144.     tr = gtk.CellRendererText()
  145.     tr.set_property("xpad", 6)
  146.     tr.set_property("ypad", 6)
  147.     cr = gtk.CellRendererToggle()
  148.     cr.set_property("activatable", True)
  149.     cr.set_property("xpad", 6)
  150.     cr.connect("toggled", self.toggled)
  151.  
  152.     column_install = gtk.TreeViewColumn("Install", cr)
  153.     column_install.set_cell_data_func (cr, self.install_column_view_func)
  154.     column = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS)
  155.     column.set_resizable(True)
  156.     major,minor,patch = gtk.pygtk_version
  157.     if (major >= 2) and (minor >= 5):
  158.       column_install.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
  159.       column_install.set_fixed_width(30)
  160.       column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
  161.       column.set_fixed_width(100)
  162.       self.treeview_update.set_fixed_height_mode(False)
  163.  
  164.     self.treeview_update.append_column(column_install)
  165.     column_install.set_visible(True)
  166.     self.treeview_update.append_column(column)
  167.     self.treeview_update.set_search_column(LIST_NAME)
  168.     self.treeview_update.connect("button-press-event", self.show_context_menu)
  169.     self.treeview_update.connect("row-activated", self.row_activated)
  170.  
  171.     # setup the help viewer and disable the help button if there
  172.     # is no viewer available
  173.     #self.help_viewer = HelpViewer("update-manager")
  174.     #if self.help_viewer.check() == False:
  175.     #    self.button_help.set_sensitive(False)
  176.  
  177.     if not os.path.exists("/usr/bin/software-properties-gtk"):
  178.         self.button_settings.set_sensitive(False)
  179.  
  180.     self.gconfclient = gconf.client_get_default()
  181.     init_proxy(self.gconfclient)
  182.     # init show version
  183.     try:
  184.         self.show_versions = self.gconfclient.get_bool("/apps/update-manager/show_versions")
  185.     except gobject.GError, e:
  186.         self.show_versions = False
  187.     # keep track when we run (for update-notifier)
  188.     try:
  189.         self.gconfclient.set_int("/apps/update-manager/launch_time", int(time.time()))
  190.     except gobject.GError, e:
  191.         print "Error setting launch_time: ", e
  192.     # get progress object
  193.     self.progress = GtkProgress.GtkOpProgress(self.dialog_cacheprogress,
  194.                                               self.progressbar_cache,
  195.                                               self.label_cache,
  196.                                               self.window_main)
  197.  
  198.     #set minimum size to prevent the headline label blocking the resize process
  199.     self.window_main.set_size_request(500,-1) 
  200.     # restore state
  201.     self.restore_state()
  202.     # deal with no-focus-on-map
  203.     if options.no_focus_on_map:
  204.         self.window_main.set_focus_on_map(False)
  205.         self.progress._window.set_focus_on_map(False)
  206.     # show the main window
  207.     self.window_main.show()
  208.     # get the install backend
  209.     self.install_backend = backend.backend_factory(self.window_main)
  210.  
  211.     # it can only the iconified *after* it is shown (even if the docs
  212.     # claim otherwise)
  213.     if options.no_focus_on_map:
  214.         self.window_main.iconify()
  215.         self.window_main.stick()
  216.         self.window_main.set_urgency_hint(True)
  217.         self.initial_focus_id = self.window_main.connect(
  218.             "focus-in-event", self.on_initial_focus_in)
  219.     else:
  220.         self.warn_on_battery()
  221.  
  222.   def on_initial_focus_in(self, widget, event):
  223.       """callback run on initial focus-in (if started unmapped)"""
  224.       widget.unstick()
  225.       widget.set_urgency_hint(False)
  226.       self.window_main.disconnect(self.initial_focus_id)
  227.       self.warn_on_battery()
  228.       return False
  229.  
  230.   def warn_on_battery(self):
  231.       """check and warn if on battery"""
  232.       if on_battery():
  233.           self.dialog_on_battery.set_transient_for(self.window_main)
  234.           res = self.dialog_on_battery.run()
  235.           self.dialog_on_battery.hide()
  236.           if res != gtk.RESPONSE_YES:
  237.               sys.exit()
  238.  
  239.   def install_column_view_func(self, cell_layout, renderer, model, iter):
  240.     pkg = model.get_value(iter, LIST_PKG)
  241.     # hide it if we are only a header line
  242.     renderer.set_property("visible", pkg != None)
  243.     if pkg is None:
  244.         return
  245.     to_install = pkg.markedInstall or pkg.markedUpgrade
  246.     renderer.set_property("active", to_install)
  247.     if pkg.name in self.list.held_back:
  248.         renderer.set_property("activatable", False)
  249.     else: 
  250.         renderer.set_property("activatable", True)
  251.  
  252.   def setupDbus(self):
  253.     """ this sets up a dbus listener if none is installed alread """
  254.     # check if there is another g-a-i already and if not setup one
  255.     # listening on dbus
  256.     try:
  257.         bus = dbus.SessionBus()
  258.     except:
  259.         print "warning: could not initiate dbus"
  260.         return
  261.     try:
  262.         proxy_obj = bus.get_object('org.freedesktop.UpdateManager', 
  263.                                    '/org/freedesktop/UpdateManagerObject')
  264.         iface = dbus.Interface(proxy_obj, 'org.freedesktop.UpdateManagerIFace')
  265.         iface.bringToFront()
  266.         #print "send bringToFront"
  267.         sys.exit(0)
  268.     except dbus.DBusException, e:
  269.          #print "no listening object (%s) "% e
  270.          bus_name = dbus.service.BusName('org.freedesktop.UpdateManager',bus)
  271.          self.dbusControler = UpdateManagerDbusControler(self, bus_name)
  272.  
  273.  
  274.   def on_checkbutton_reminder_toggled(self, checkbutton):
  275.     self.gconfclient.set_bool("/apps/update-manager/remind_reload",
  276.                               not checkbutton.get_active())
  277.  
  278.   def close(self, widget, data=None):
  279.     if self.window_main.get_property("sensitive") is False:
  280.         return True
  281.     else:
  282.         self.exit()
  283.  
  284.   
  285.   def set_changes_buffer(self, changes_buffer, text, name, srcpkg):
  286.     changes_buffer.set_text("")
  287.     lines = text.split("\n")
  288.     if len(lines) == 1:
  289.       changes_buffer.set_text(text)
  290.       return
  291.     
  292.     for line in lines:
  293.       end_iter = changes_buffer.get_end_iter()
  294.       version_match = re.match(r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line)
  295.       #bullet_match = re.match("^.*[\*-]", line)
  296.       author_match = re.match("^.*--.*<.*@.*>.*$", line)
  297.       if version_match:
  298.         version = version_match.group(1)
  299.     upload_archive = version_match.group(2).strip()
  300.         version_text = _("Version %s: \n") % version
  301.         changes_buffer.insert_with_tags_by_name(end_iter, version_text, "versiontag")
  302.       elif (author_match):
  303.         pass
  304.       else:
  305.         changes_buffer.insert(end_iter, line+"\n")
  306.         
  307.  
  308.   def on_treeview_update_cursor_changed(self, widget):
  309.     path = widget.get_cursor()[0]
  310.     # check if we have a path at all
  311.     if path == None:
  312.       return
  313.     model = widget.get_model()
  314.     iter = model.get_iter(path)
  315.  
  316.     # set descr
  317.     pkg = model.get_value(iter, LIST_PKG)
  318.     if pkg == None or pkg.description == None:
  319.       changes_buffer = self.textview_changes.get_buffer()
  320.       changes_buffer.set_text("")
  321.       desc_buffer = self.textview_descr.get_buffer()
  322.       desc_buffer.set_text("")
  323.       self.notebook_details.set_sensitive(False)
  324.       return
  325.     long_desc = pkg.description
  326.     self.notebook_details.set_sensitive(True)
  327.     # do some regular expression magic on the description
  328.     # Add a newline before each bullet
  329.     p = re.compile(r'^(\s|\t)*(\*|0|-)',re.MULTILINE)
  330.     long_desc = p.sub('\n*', long_desc)
  331.     # replace all newlines by spaces
  332.     p = re.compile(r'\n', re.MULTILINE)
  333.     long_desc = p.sub(" ", long_desc)
  334.     # replace all multiple spaces by newlines
  335.     p = re.compile(r'\s\s+', re.MULTILINE)
  336.     long_desc = p.sub("\n", long_desc)
  337.  
  338.     desc_buffer = self.textview_descr.get_buffer()
  339.     desc_buffer.set_text(long_desc)
  340.  
  341.     # now do the changelog
  342.     name = model.get_value(iter, LIST_NAME)
  343.     if name == None:
  344.       return
  345.  
  346.     changes_buffer = self.textview_changes.get_buffer()
  347.     
  348.     # check if we have the changes already
  349.     if self.cache.all_changes.has_key(name):
  350.       changes = self.cache.all_changes[name]
  351.       self.set_changes_buffer(changes_buffer, changes[0], name, changes[1])
  352.     else:
  353.       if self.expander_details.get_expanded():
  354.         lock = thread.allocate_lock()
  355.         lock.acquire()
  356.         t=thread.start_new_thread(self.cache.get_news_and_changelog,(name,lock))
  357.         changes_buffer.set_text("%s\n" % _("Downloading list of changes..."))
  358.         iter = changes_buffer.get_iter_at_line(1)
  359.         anchor = changes_buffer.create_child_anchor(iter)
  360.         button = gtk.Button(stock="gtk-cancel")
  361.         self.textview_changes.add_child_at_anchor(button, anchor)
  362.         button.show()
  363.         id = button.connect("clicked",
  364.                             lambda w,lock: lock.release(), lock)
  365.         # wait for the dl-thread
  366.         while lock.locked():
  367.           time.sleep(0.01)
  368.           while gtk.events_pending():
  369.             gtk.main_iteration()
  370.         # download finished (or canceld, or time-out)
  371.         button.hide()
  372.         button.disconnect(id);
  373.     # check if we still are in the right pkg (the download may have taken
  374.     # some time and the user may have clicked on a new pkg)
  375.     path  = widget.get_cursor()[0]
  376.     if path == None:
  377.       return
  378.     now_name = widget.get_model()[path][LIST_NAME]
  379.     if name != now_name:
  380.         return
  381.     # display NEWS.Debian first, then the changelog
  382.     changes = ""
  383.     srcpkg = self.cache[name].sourcePackageName
  384.     if self.cache.all_news.has_key(name):
  385.         changes += self.cache.all_news[name]
  386.     if self.cache.all_changes.has_key(name):
  387.         changes += self.cache.all_changes[name]
  388.     if changes:
  389.         self.set_changes_buffer(changes_buffer, changes, name, srcpkg)
  390.  
  391.   def show_context_menu(self, widget, event):
  392.     """
  393.     Show a context menu if a right click was performed on an update entry
  394.     """
  395.     if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
  396.         menu = gtk.Menu()
  397.         item_select_none = gtk.MenuItem(_("_Uncheck All"))
  398.         item_select_none.connect("activate", self.select_none_updgrades)
  399.         menu.add(item_select_none)
  400.         num_updates = self.cache.installCount
  401.         if num_updates == 0:
  402.             item_select_none.set_property("sensitive", False)
  403.         item_select_all = gtk.MenuItem(_("_Check All"))
  404.         item_select_all.connect("activate", self.select_all_updgrades)
  405.         menu.add(item_select_all)
  406.         menu.popup(None, None, None, 0, event.time)
  407.         menu.show_all()
  408.         return True
  409.  
  410.   def select_all_updgrades(self, widget):
  411.     """
  412.     Select all updates
  413.     """
  414.     self.setBusy(True)
  415.     self.cache.saveDistUpgrade()
  416.     self.treeview_update.queue_draw()
  417.     self.refresh_updates_count()
  418.     self.setBusy(False)
  419.  
  420.   def select_none_updgrades(self, widget):
  421.     """
  422.     Select none updates
  423.     """
  424.     self.setBusy(True)
  425.     self.cache.clear()
  426.     self.treeview_update.queue_draw()
  427.     self.refresh_updates_count()
  428.     self.setBusy(False)
  429.  
  430.   def setBusy(self, flag):
  431.       """ Show a watch cursor if the app is busy for more than 0.3 sec.
  432.       Furthermore provide a loop to handle user interface events """
  433.       if self.window_main.window is None:
  434.           return
  435.       if flag == True:
  436.           self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  437.       else:
  438.           self.window_main.window.set_cursor(None)
  439.       while gtk.events_pending():
  440.           gtk.main_iteration()
  441.  
  442.   def refresh_updates_count(self):
  443.       self.button_install.set_sensitive(self.cache.installCount)
  444.       try:
  445.           inst_count = self.cache.installCount
  446.           self.dl_size = self.cache.requiredDownload
  447.           t = _("%s selected. Download size: %s") % (inst_count,
  448.                                                        humanize_size(self.dl_size))
  449.           self.label_downsize.set_text(t)
  450.       except SystemError, e:
  451.           print "requiredDownload could not be calculated: %s" % e
  452.           self.label_downsize.set_markup(_("Unknown download size"))
  453.  
  454.   def _get_last_apt_get_update_text(self):
  455.       """
  456.       return a human readable string with the information when
  457.       the last apt-get update was run
  458.       """
  459.       if not os.path.exists("/var/lib/apt/periodic/update-success-stamp"):
  460.           return _("It is unknown when the package information was "
  461.                    "updated last. Please try clicking on the 'Check' "
  462.                    "button to update the information.")
  463.       # calculate when the last apt-get update (or similar operation)
  464.       # was performed
  465.       mtime = os.stat("/var/lib/apt/periodic/update-success-stamp")[stat.ST_MTIME]
  466.       ago_days = int( (time.time() - mtime) / (24*60*60))
  467.       ago_hours = int((time.time() - mtime) / (60*60) )
  468.       if ago_days > 0:
  469.           return ngettext("The package information was last updated %s day ago.",
  470.                           "The package information was last updated %s days ago.",
  471.                           ago_days) % ago_days
  472.       elif ago_hours > 0:
  473.           return ngettext("The package information was last updated %s hour ago.",
  474.                           "The package information was last updated %s hours ago.",
  475.                           ago_hours) % ago_hours
  476.       else:
  477.           return _("The package information was last updated less than one hour ago.")
  478.       return None
  479.  
  480.   def update_last_updated_text(self):
  481.       """timer that updates the last updated text """
  482.       #print "update_last_updated_text"
  483.       num_updates = self.cache.installCount
  484.       if num_updates == 0:
  485.           if self._get_last_apt_get_update_text() is not None:
  486.               text_label_main = self._get_last_apt_get_update_text()
  487.               self.label_main_details.set_text(text_label_main)
  488.           return True
  489.       # stop the timer if there are upgrades now
  490.       return False
  491.  
  492.   def update_count(self):
  493.       """activate or disable widgets and show dialog texts correspoding to
  494.          the number of available updates"""
  495.       self.refresh_updates_count()
  496.       num_updates = self.cache.installCount
  497.       text_label_main = _("Software updates correct errors, eliminate security vulnerabilities and provide new features.")
  498.       if num_updates == 0:
  499.           text_header= "<big><b>%s</b></big>"  % _("Your system is up-to-date")
  500.           self.label_downsize.set_text("")
  501.           self.notebook_details.set_sensitive(False)
  502.           self.treeview_update.set_sensitive(False)
  503.           self.button_install.set_sensitive(False)
  504.           self.button_close.grab_default()
  505.           self.textview_changes.get_buffer().set_text("")
  506.           self.textview_descr.get_buffer().set_text("")
  507.           if self._get_last_apt_get_update_text() is not None:
  508.               text_label_main = self._get_last_apt_get_update_text()
  509.           # add timer to ensure we update the information when the 
  510.           # last package count update was performed
  511.           glib.timeout_add_seconds(10, self.update_last_updated_text)
  512.       else:
  513.           # show different text on first run (UX team suggestion)
  514.           firstrun = self.gconfclient.get_bool("/apps/update-manager/first_run")
  515.           if firstrun:
  516.               text_header = "<big><b>%s</b></big>" % _("Welcome to Ubuntu")
  517.               text_label_main = _("These software updates have been issued since Ubuntu was released. If you don't want to install them now, choose \"Update Manager\" from the Administration Menu later.")
  518.               self.gconfclient.set_bool("/apps/update-manager/first_run", False)
  519.           else:
  520.               text_header = "<big><b>%s</b></big>" % _("Software updates are available for this computer")
  521.               text_label_main = _("If you don't want to install them now, choose \"Update Manager\" from the Administration menu later.")
  522.           self.notebook_details.set_sensitive(True)
  523.           self.treeview_update.set_sensitive(True)
  524.           self.button_install.grab_default()
  525.           self.treeview_update.set_cursor(1)
  526.       self.label_header.set_markup(text_header)
  527.       self.label_main_details.set_text(text_label_main)
  528.       return True
  529.  
  530.   def activate_details(self, expander, data):
  531.     expanded = self.expander_details.get_expanded()
  532.     self.vbox_updates.set_child_packing(self.expander_details,
  533.                                         expanded,
  534.                                         True,
  535.                                         0,
  536.                                         True)
  537.     self.gconfclient.set_bool("/apps/update-manager/show_details",expanded)
  538.     if expanded:
  539.       self.on_treeview_update_cursor_changed(self.treeview_update)
  540.  
  541.   def on_button_reload_clicked(self, widget):
  542.     #print "on_button_reload_clicked"
  543.     self.check_metarelease()
  544.     self.invoke_manager(UPDATE)
  545.  
  546.   #def on_button_help_clicked(self, widget):
  547.   #  self.help_viewer.run()
  548.  
  549.   def on_button_settings_clicked(self, widget):
  550.     #print "on_button_settings_clicked"
  551.     try:
  552.         apt_pkg.PkgSystemUnLock()
  553.     except SystemError:
  554.         pass
  555.     cmd = ["/usr/bin/gksu", 
  556.            "--desktop", "/usr/share/applications/software-properties.desktop", 
  557.            "--", "/usr/bin/software-properties-gtk","--open-tab","2",
  558.            "--toplevel", "%s" % self.window_main.window.xid ]
  559.     self.window_main.set_sensitive(False)
  560.     p = subprocess.Popen(cmd)
  561.     while p.poll() is None:
  562.         while gtk.events_pending():
  563.             gtk.main_iteration()
  564.         time.sleep(0.05)
  565.     self.fillstore()
  566.  
  567.   def on_button_install_clicked(self, widget):
  568.     #print "on_button_install_clicked"
  569.     err_sum = _("Not enough free disk space")
  570.     err_long= _("The upgrade needs a total of %s free space on disk '%s'. "
  571.                 "Please free at least an additional %s of disk "
  572.                 "space on '%s'. "
  573.                 "Empty your trash and remove temporary "
  574.                 "packages of former installations using "
  575.                 "'sudo apt-get clean'.")
  576.     # check free space and error if its not enough
  577.     try:
  578.         self.cache.checkFreeSpace()
  579.     except NotEnoughFreeSpaceError, e:
  580.         for req in e.free_space_required_list:
  581.             self.error(err_sum, err_long % (req.size_total,
  582.                                             req.dir,
  583.                                             req.size_needed,
  584.                                             req.dir))
  585.         return
  586.     self.invoke_manager(INSTALL)
  587.     
  588.   def invoke_manager(self, action):
  589.     # check first if no other package manager is runing
  590.  
  591.     # don't display apt-listchanges, we already showed the changelog
  592.     os.environ["APT_LISTCHANGES_FRONTEND"]="none"
  593.  
  594.     # Do not suspend during the update process
  595.     (dev, cookie) = inhibit_sleep()
  596.  
  597.     # set window to insensitive
  598.     self.window_main.set_sensitive(False)
  599.     self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  600.  
  601.     # do it
  602.     if action == UPDATE:
  603.         self.install_backend.update()
  604.     elif action == INSTALL:
  605.         self.install_backend.commit(self.cache)
  606.  
  607.     s = _("Reading package information")
  608.     self.label_cache_progress_title.set_label("<b><big>%s</big></b>" % s)
  609.     self.fillstore()
  610.  
  611.     # Allow suspend after synaptic is finished
  612.     if cookie != False:
  613.         allow_sleep(dev, cookie)
  614.     self.window_main.set_sensitive(True)
  615.     self.window_main.window.set_cursor(None)
  616.  
  617.   def row_activated(self, treeview, path, column):
  618.       iter = self.store.get_iter(path)
  619.       pkg = self.store.get_value(iter, LIST_PKG)
  620.       origin = self.store.get_value(iter, LIST_ORIGIN)
  621.       if pkg is not None:
  622.           return
  623.       self.setBusy(True)
  624.       actiongroup = apt_pkg.GetPkgActionGroup(self.cache._depcache)
  625.       for pkg in self.list.pkgs[origin]:
  626.           if pkg.markedInstall or pkg.markedUpgrade:
  627.               #print "marking keep: ", pkg.name
  628.               pkg.markKeep()
  629.           elif not (pkg.name in self.list.held_back):
  630.               #print "marking install: ", pkg.name
  631.               pkg.markInstall(autoFix=False,autoInst=False)
  632.       # check if we left breakage
  633.       if self.cache._depcache.BrokenCount:
  634.           Fix = apt_pkg.GetPkgProblemResolver(self.cache._depcache)
  635.           Fix.ResolveByKeep()
  636.       self.refresh_updates_count()
  637.       self.treeview_update.queue_draw()
  638.       del actiongroup
  639.       self.setBusy(False)
  640.  
  641.  
  642.   def toggled(self, renderer, path):
  643.     """ a toggle button in the listview was toggled """
  644.     iter = self.store.get_iter(path)
  645.     pkg = self.store.get_value(iter, LIST_PKG)
  646.     # make sure that we don't allow to toggle deactivated updates
  647.     # this is needed for the call by the row activation callback
  648.     if pkg is None or pkg.name in self.list.held_back:
  649.         return False
  650.     self.setBusy(True)
  651.     # update the cache
  652.     if pkg.markedInstall or pkg.markedUpgrade:
  653.         pkg.markKeep()
  654.         if self.cache._depcache.BrokenCount:
  655.             Fix = apt_pkg.GetPkgProblemResolver(self.cache._depcache)
  656.             Fix.ResolveByKeep()
  657.     else:
  658.         pkg.markInstall()
  659.     self.treeview_update.queue_draw()
  660.     self.refresh_updates_count()
  661.     self.setBusy(False)
  662.  
  663.   def on_treeview_update_row_activated(self, treeview, path, column, *args):
  664.     """
  665.     If an update row was activated (by pressing space), toggle the 
  666.     install check box
  667.     """
  668.     self.toggled(None, path)
  669.  
  670.   def on_window_main_size_allocate(self,arg1,arg2):
  671.     """ 
  672.     recalculates headline labels on window resize to work around
  673.     problems with gtk word wrapping 
  674.     (http://bugzilla.gnome.org/show_bug.cgi?id=101968)
  675.     """
  676.     # this number is based on border width 
  677.     # (2*main_border_with + icon width + 2*icon_border_with)
  678.     #     2*18 + 48 + 2*12 
  679.     border_space = 96
  680.     width, height = self.window_main.get_size()
  681.     #print "on_window_main_size_allocate", width, height
  682.     self.label_main_details.set_size_request(width - border_space ,-1)
  683.     self.label_header.set_size_request(width - border_space,-1)
  684.  
  685.   def exit(self):
  686.     """ exit the application, save the state """
  687.     self.save_state()
  688.     #gtk.main_quit()
  689.     sys.exit(0)
  690.  
  691.   def save_state(self):
  692.     """ save the state  (window-size for now) """
  693.     (x,y) = self.window_main.get_size()
  694.     try:
  695.         self.gconfclient.set_pair("/apps/update-manager/window_size",
  696.                                   gconf.VALUE_INT, gconf.VALUE_INT, x, y)
  697.     except gobject.GError, e:
  698.         print "Could not save the configuration to gconf: %s" % e
  699.         pass
  700.  
  701.   def restore_state(self):
  702.     """ restore the state (window-size for now) """
  703.     expanded = self.gconfclient.get_bool("/apps/update-manager/show_details")
  704.     self.expander_details.set_expanded(expanded)
  705.     self.vbox_updates.set_child_packing(self.expander_details,
  706.                                         expanded,
  707.                                         True,
  708.                                         0,
  709.                                         True)
  710.     (x,y) = self.gconfclient.get_pair("/apps/update-manager/window_size",
  711.                                       gconf.VALUE_INT, gconf.VALUE_INT)
  712.     if x > 0 and y > 0:
  713.       self.window_main.resize(x,y)
  714.  
  715.   def fillstore(self):
  716.     # use the watch cursor
  717.     self.setBusy(True)
  718.     # clean most objects
  719.     self.dl_size = 0
  720.     try:
  721.         self.initCache()
  722.     except SystemError, e:
  723.         msg = ("<big><b>%s</b></big>\n\n%s\n'%s'" %
  724.                (_("Could not initialize the package information"),
  725.                 _("An unresolvable problem occurred while "
  726.                   "initializing the package information.\n\n"
  727.                   "Please report this bug against the 'update-manager' "
  728.                   "package and include the following error message:\n"),
  729.                 e)
  730.                )
  731.         dialog = gtk.MessageDialog(self.window_main,
  732.                                    0, gtk.MESSAGE_ERROR,
  733.                                    gtk.BUTTONS_CLOSE,"")
  734.         dialog.set_markup(msg)
  735.         dialog.vbox.set_spacing(6)
  736.         dialog.run()
  737.         dialog.destroy()
  738.         sys.exit(1)
  739.     self.store.clear()
  740.     self.list = UpdateList(self)
  741.     # fill them again
  742.     try:
  743.         self.list.update(self.cache)
  744.     except SystemError, e:
  745.         msg = ("<big><b>%s</b></big>\n\n%s\n'%s'" %
  746.                (_("Could not calculate the upgrade"),
  747.                 _("An unresolvable problem occurred while "
  748.                   "calculating the upgrade.\n\n"
  749.                   "Please report this bug against the 'update-manager' "
  750.                   "package and include the following error message:"),
  751.                 e)
  752.                )
  753.         dialog = gtk.MessageDialog(self.window_main,
  754.                                    0, gtk.MESSAGE_ERROR,
  755.                                    gtk.BUTTONS_CLOSE,"")
  756.         dialog.set_markup(msg)
  757.         dialog.vbox.set_spacing(6)
  758.         dialog.run()
  759.         dialog.destroy()
  760.     if self.list.num_updates > 0:
  761.       origin_list = self.list.pkgs.keys()
  762.       origin_list.sort(lambda x,y: cmp(x.importance,y.importance))
  763.       origin_list.reverse()
  764.       for origin in origin_list:
  765.         self.store.append(['<b><big>%s</big></b>' % origin.description,
  766.                            origin.description, None, origin])
  767.         for pkg in self.list.pkgs[origin]:
  768.           name = xml.sax.saxutils.escape(pkg.name)
  769.           if not pkg.isInstalled:
  770.               name += _(" (New install)")
  771.           summary = xml.sax.saxutils.escape(pkg.summary)
  772.           contents = "<b>%s</b>\n<small>%s</small>" % (name, summary)
  773.           #TRANSLATORS: the b stands for Bytes
  774.           size = _("(Size: %s)") % humanize_size(pkg.packageSize)
  775.           if pkg.installedVersion != None:
  776.               version = _("From version %(old_version)s to %(new_version)s") %\
  777.                   {"old_version" : pkg.installedVersion,
  778.                    "new_version" : pkg.candidateVersion}
  779.           else:
  780.               version = _("Version %s") % pkg.candidateVersion
  781.           if self.show_versions:
  782.               contents = "%s\n<small>%s %s</small>" % (contents, version, size)
  783.           else:
  784.               contents = "%s <small>%s</small>" % (contents, size)
  785.           self.store.append([contents, pkg.name, pkg, None])
  786.     self.update_count()
  787.     self.setBusy(False)
  788.     self.check_all_updates_installable()
  789.     return False
  790.  
  791.   def dist_no_longer_supported(self, meta_release):
  792.     msg = "<big><b>%s</b></big>\n\n%s" % \
  793.           (_("Your Ubuntu release is not supported anymore"),
  794.        _("You will not get any further security fixes or critical "
  795.              "updates. "
  796.              "Please Upgrade to a later version of Ubuntu Linux."))
  797.     dialog = gtk.MessageDialog(self.window_main, 0, gtk.MESSAGE_WARNING,
  798.                                gtk.BUTTONS_CLOSE,"")
  799.     dialog.set_title("")
  800.     dialog.set_markup(msg)
  801.     button = gtk.LinkButton(_("Upgrade information"))
  802.     button.set_uri("http://www.ubuntu.com/getubuntu/upgrading")
  803.     button.show()
  804.     dialog.get_content_area().pack_end(button)
  805.     dialog.run()
  806.     dialog.destroy()
  807.  
  808.   def error(self, summary, details):
  809.       " helper function to display a error message "
  810.       msg = ("<big><b>%s</b></big>\n\n%s\n" % (summary, details) )
  811.       dialog = gtk.MessageDialog(self.window_main,
  812.                                  0, gtk.MESSAGE_ERROR,
  813.                                  gtk.BUTTONS_CLOSE,"")
  814.       dialog.set_markup(msg)
  815.       dialog.vbox.set_spacing(6)
  816.       dialog.run()
  817.       dialog.destroy()
  818.  
  819.   def on_button_dist_upgrade_clicked(self, button):
  820.       #print "on_button_dist_upgrade_clicked"
  821.       fetcher = DistUpgradeFetcherGtk(new_dist=self.new_dist, parent=self, progress=GtkProgress.GtkFetchProgress(self))
  822.       if self.options.sandbox:
  823.           fetcher.run_options.append("--sandbox")
  824.       fetcher.run()
  825.       
  826.   def new_dist_available(self, meta_release, upgradable_to):
  827.     self.frame_new_release.show()
  828.     self.label_new_release.set_markup(_("<b>New Ubuntu release '%s' is available</b>") % upgradable_to.version)
  829.     self.new_dist = upgradable_to
  830.     
  831.  
  832.   # fixme: we should probably abstract away all the stuff from libapt
  833.   def initCache(self): 
  834.     # get the lock
  835.     try:
  836.         apt_pkg.PkgSystemLock()
  837.     except SystemError, e:
  838.         pass
  839.         #d = gtk.MessageDialog(parent=self.window_main,
  840.         #                      flags=gtk.DIALOG_MODAL,
  841.         #                      type=gtk.MESSAGE_ERROR,
  842.         #                      buttons=gtk.BUTTONS_CLOSE)
  843.         #d.set_markup("<big><b>%s</b></big>\n\n%s" % (
  844.         #    _("Only one software management tool is allowed to "
  845.         #      "run at the same time"),
  846.         #    _("Please close the other application e.g. 'aptitude' "
  847.         #      "or 'Synaptic' first.")))
  848.         #print "error from apt: '%s'" % e
  849.         #d.set_title("")
  850.         #res = d.run()
  851.         #d.destroy()
  852.         #sys.exit()
  853.  
  854.     try:
  855.         if hasattr(self, "cache"):
  856.             self.cache.open(self.progress)
  857.             self.cache._initDepCache()
  858.         else:
  859.             self.cache = MyCache(self.progress)
  860.     except AssertionError:
  861.         # if the cache could not be opened for some reason,
  862.         # let the release upgrader handle it, it deals
  863.         # a lot better with this
  864.         self.ask_run_partial_upgrade()
  865.         # we assert a clean cache
  866.         msg=("<big><b>%s</b></big>\n\n%s"% \
  867.              (_("Software index is broken"),
  868.               _("It is impossible to install or remove any software. "
  869.                 "Please use the package manager \"Synaptic\" or run "
  870.         "\"sudo apt-get install -f\" in a terminal to fix "
  871.         "this issue at first.")))
  872.         dialog = gtk.MessageDialog(self.window_main,
  873.                                    0, gtk.MESSAGE_ERROR,
  874.                                    gtk.BUTTONS_CLOSE,"")
  875.         dialog.set_markup(msg)
  876.         dialog.vbox.set_spacing(6)
  877.         dialog.run()
  878.         dialog.destroy()
  879.         sys.exit(1)
  880.     else:
  881.         self.progress.hide()
  882.  
  883.   def check_auto_update(self):
  884.       # Check if automatic update is enabled. If not show a dialog to inform
  885.       # the user about the need of manual "reloads"
  886.       remind = self.gconfclient.get_bool("/apps/update-manager/remind_reload")
  887.       if remind == False:
  888.           return
  889.  
  890.       update_days = apt_pkg.Config.FindI("APT::Periodic::Update-Package-Lists")
  891.       if update_days < 1:
  892.           self.dialog_manual_update.set_transient_for(self.window_main)
  893.           res = self.dialog_manual_update.run()
  894.           self.dialog_manual_update.hide()
  895.           if res == gtk.RESPONSE_YES:
  896.               self.on_button_reload_clicked(None)
  897.  
  898.   def check_all_updates_installable(self):
  899.     """ Check if all available updates can be installed and suggest
  900.         to run a distribution upgrade if not """
  901.     if self.list.distUpgradeWouldDelete > 0:
  902.         self.ask_run_partial_upgrade()
  903.  
  904.   def ask_run_partial_upgrade(self):
  905.       self.dialog_dist_upgrade.set_transient_for(self.window_main)
  906.       res = self.dialog_dist_upgrade.run()
  907.       self.dialog_dist_upgrade.hide()
  908.       if res == gtk.RESPONSE_YES:
  909.           os.execl("/usr/bin/gksu",
  910.                    "/usr/bin/gksu", "--desktop",
  911.                    "/usr/share/applications/update-manager.desktop",
  912.                    "--", "/usr/bin/update-manager", "--dist-upgrade")
  913.       return False
  914.  
  915.   def check_metarelease(self):
  916.       " check for new meta-release information "
  917.       gconfclient = gconf.client_get_default()
  918.       self.meta = MetaRelease(self.options.devel_release,
  919.                               self.options.use_proposed)
  920.       self.meta.connect("dist_no_longer_supported",self.dist_no_longer_supported)
  921.       # check if we are interessted in dist-upgrade information
  922.       # (we are not by default on dapper)
  923.       if self.options.check_dist_upgrades or \
  924.              gconfclient.get_bool("/apps/update-manager/check_dist_upgrades"):
  925.           self.meta.connect("new_dist_available",self.new_dist_available)
  926.       
  927.  
  928.   def main(self, options):
  929.     self.options = options
  930.  
  931.     # check for new distributin information
  932.     self.check_metarelease()
  933.  
  934.     while gtk.events_pending():
  935.       gtk.main_iteration()
  936.  
  937.     self.fillstore()
  938.     self.check_auto_update()
  939.     gtk.main()
  940.